<?php
/*
    Microsoft SQL Server Driver for PHP - Unit Test Framework
    Copyright (c) Microsoft Corporation.  All rights reserved.

    Description:
        Common functions (shared by all tests).

*/

require_once('MsHelper.inc');
require_once('MsSetup.inc');

$tvpIncPath = dirname(__FILE__).DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR.'inc'.DIRECTORY_SEPARATOR;

require_once($tvpIncPath. 'test_tvp_data.php');

$usingUTF8data = false;

function isWindows()
{
    // This method returns TRUE when running in a Windows platform
    // The possible values are WIN32, WINNT and Windows
    return (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN');
}

function useUTF8Data()
{
    global $usingUTF8data;
    return $usingUTF8data;
}

function setUTF8Data($val)
{
    global $usingUTF8data;
    $usingUTF8data = $val;
}

function testMode()
{
    $testMode = getenv('PHPT_EXEC');
    return ($testMode ? true : false);
}

function traceMode()
{
    global $traceEnabled;
    return ((!testMode() && $traceEnabled) ? true : false);
}

function trace($msg)
{
    if (traceMode()) {
        echo $msg;
    }
}

/**
 * This method prints the message when traceMode() is true
 *
 * @param  string  $msg
 * @return void
 */
function traceData($sqlType, $data)
{
    if (traceMode()) {
        $msg = strtoupper(" $sqlType:");
        echo "$msg\t";
        if (strlen($msg) <= 7) {
            echo "\t";
        }
        if (strlen($msg) <= 15) {
            echo "\t";
        }
        echo "$data\n";
    }
}

function isMarsSupported()
{
    global $marsMode;
    return ($marsMode ? true : false);
}

function isDaasMode()
{
    global $daasMode;
    return ($daasMode ? true : false);
}

function isLocaleDisabled()
{
    global $daasMode, $localeDisabled;
    return ($daasMode || $localeDisabled);
}

function isServerHGSEnabled()
{
    $conn = connect();
    $tsql = "SELECT @@SERVERNAME";
    $stmt = sqlsrv_query($conn, $tsql);

    if (sqlsrv_fetch($stmt)) {
        $name = sqlsrv_get_field($stmt, 0);
        if (strpos($name, 'HGS') != false) {
            return true;
        }
    }
    return false;
}

function isSQLAzure()
{
    // 'SQL Azure' indicates SQL Database or SQL Data Warehouse
    // For details, https://docs.microsoft.com/sql/t-sql/functions/serverproperty-transact-sql
    $conn = connect();
    $tsql = "SELECT SERVERPROPERTY ('edition')";
    $stmt = sqlsrv_query($conn, $tsql);

    if (sqlsrv_fetch($stmt)) {
        $edition = sqlsrv_get_field($stmt, 0);
        if ($edition === "SQL Azure") {
            return true;
        } else {
            return false;
        }
    } else {
        die("Could not fetch server property.");
    }
}

function isAzureDW()
{
    // Check if running Azure Data Warehouse
    // For details, https://docs.microsoft.com/sql/t-sql/functions/serverproperty-transact-sql
    $conn = connect();
    $tsql = "SELECT SERVERPROPERTY ('edition'), SERVERPROPERTY ('EngineEdition')";
    $stmt = sqlsrv_query($conn, $tsql);

    if (sqlsrv_fetch($stmt)) {
        $edition = sqlsrv_get_field($stmt, 0);
        $engEd = sqlsrv_get_field($stmt, 1, SQLSRV_PHPTYPE_INT);
        
        if ($edition == "SQL Azure" && $engEd == 6) {
            return true;
        } else {
            return false;
        }
    } else {
        die("Could not fetch edition info.");
    }
}

function startTest($testName)
{
    if (traceMode()) {
        echo "Starting \"$testName\" test...\n\n";
    }
}

/**
 * This method signals the end of a test given its name
 *
 * @return void
 */
function endTest($testName)
{
    echo "Test \"$testName\" completed successfully.\n";
}

function setup()
{
    set_time_limit(0);
    sqlsrv_configure('LogSubsystems', SQLSRV_LOG_SYSTEM_OFF);
    sqlsrv_configure('WarningsReturnAsErrors', 1);
}

function configure($param, $expected)
{
    sqlsrv_configure($param, $expected);
    $actual = sqlsrv_get_config($param);

    if ($actual == $expected) {
        trace("Set configuration parameter $param = $actual.\n");
    } else {
        die("Failed to set configuration parameter $param = $expected.");
    }
}

function connect($options = array())
{
    include('MsSetup.inc');

    if (!empty($options)) {
        $connectionOptions = array_merge($connectionOptions, $options);
    }

    trace("Attempting connection to $server...");
    $conn = sqlsrv_connect($server, $connectionOptions);
    if ($conn === false) {
        fatalError("Failed to connect to $server.");
    }
    trace(" successfully connected.\n\n");
    return ($conn);
}

function getTempTableName($table = '', $temporary = true)
{
    // A temporary table name with the '#' prefix will be automatically
    // dropped once the connection is closed. Otherwise, the caller
    // should take care of dropping the temp table afterwards.

    $someNumber = rand(0, 1000);

    $prefix = '';
    if ($temporary) {
        $prefix = '#';
    }

    if (strlen($table) == 0) {
        $table = 'php_test_table';
    }

    return $prefix . $table . '_' . $someNumber;
}

function getTempProcName($proc = '', $temporary = true)
{
    // A temporary stored procedure name with the '#' prefix will be
    // automatically dropped once the connection is closed. Otherwise,
    // the caller should take care of dropping the temp procedure afterwards.

    $someNumber = rand(0, 1000);

    $prefix = '';
    if ($temporary) {
        $prefix = '#';
    }

    if (strlen($proc) == 0) {
        $proc = 'php_test_proc';
    }

    return $prefix . $proc . '_' . $someNumber;
}

function executeQuery($conn, $query)
{
    $stmt = sqlsrv_query($conn, $query);
    if ($stmt === false) {
        fatalError("Query execution failed: $query");
    }
    return ($stmt);
}


function prepareQuery($conn, $query)
{
    $stmt = sqlsrv_prepare($conn, $query);
    if ($stmt === false) {
        fatalError("Query preparation failed: $query");
    }
    return ($stmt);
}


function executeQueryEx($conn, $query, $modeDirect)
{
    if ($modeDirect) {   // direct execution
        $stmt = sqlsrv_query($conn, $query);
    } else {
        $stmt = prepareQuery($conn, $query);
        sqlsrv_execute($stmt);
    }
    return ($stmt);
}

function createTableEx($conn, $tableName, $dataType)
{
    $sql = "CREATE TABLE [$tableName] ($dataType)";
    dropTable($conn, $tableName);
    $stmt = sqlsrv_query($conn, $sql);
    if ($stmt === false) {
        fatalError("Failed to create test table: ".$sql);
    }
    sqlsrv_free_stmt($stmt);
}


function createTableIndex($conn, $tableName, $colIndex)
{
    include 'MsSetup.inc';
    createTableIndexEx($conn, $tableName, $tableIndex, $colIndex);
}


function createTableIndexEx($conn, $tableName, $tableIndex, $colIndex)
{
    trace("Creating table index for $tableName ...");
    $sqlIndex = "CREATE CLUSTERED INDEX [$tableIndex] ON [$tableName]($colIndex)";
    $stmt = sqlsrv_query($conn, $sqlIndex);
    if ($stmt === false) {
        fatalError("Failed to create clustered index for test table: ".$sqlIndex);
    }
    sqlsrv_free_stmt($stmt);
    trace(" completed successfully.\n");
}

function createUniqueIndex($conn, $tableName, $colIndex)
{
    include 'MsSetup.inc';
    createUniqueIndexEx($conn, $tableName, $tableIndex, $colIndex);
}

function createUniqueIndexEx($conn, $tableName, $tableIndex, $colIndex)
{
    trace("Creating unique table index for $tableName ...");
    $sqlIndex = "CREATE UNIQUE INDEX [$tableIndex] ON [$tableName]($colIndex)";
    $stmt = sqlsrv_query($conn, $sqlIndex);
    if ($stmt === false) {
        fatalError("Failed to create unique index for test table: ".$sqlIndex);
    }
    sqlsrv_free_stmt($stmt);
    trace(" completed successfully.\n");
}

function dropTable($conn, $tableName)
{
    $stmt = sqlsrv_query($conn, "IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'" . $tableName . "') AND type in (N'U')) DROP TABLE [$tableName]");
    if ($stmt === false) {
    } else {
        sqlsrv_free_stmt($stmt);
    }
}

function selectFromTable($conn, $tableName)
{
    return (selectFromTableEx($conn, $tableName, null));
}

function selectFromTableEx($conn, $tableName, $cond)
{
    if (($cond != null) && (strlen($cond) > 0)) {
        return (selectQuery($conn, "SELECT * FROM [$tableName] WHERE $cond"));
    } else {
        return (selectQuery($conn, "SELECT * FROM [$tableName]"));
    }
}

function selectQuery($conn, $query)
{
    return (selectQueryEx($conn, $query, null));
}

function selectQueryEx($conn, $query, $options)
{
    if ($options != null) {
        $stmt = sqlsrv_query($conn, $query, null, $options);
    } else {
        $stmt = sqlsrv_query($conn, $query);
    }
    if ($stmt === false) {
        fatalError("Failed to query test table");
    }

    $numFields = sqlsrv_num_fields($stmt);
    if ($numFields <= 0) {
        die("Unexpected number of fields: ".$numFields);
    }
    return ($stmt);
}

function rowCount($stmt)
{
    $rowCount = 0;
    while (sqlsrv_fetch($stmt)) {
        $rowCount++;
    }
    return ($rowCount);
}


function numRows($conn, $tableName)
{
    $stmt = SelectFromTable($conn, $tableName);
    $rowCount = rowCount($stmt);
    sqlsrv_free_stmt($stmt);

    return ($rowCount);
}

function insertCheck($stmt)
{
    if ($stmt === false) {
        fatalError("Failed to insert row into test table");
    }
    $numRows = sqlsrv_rows_affected($stmt);
    sqlsrv_free_stmt($stmt);
    if ($numRows != 1) {
        die("Unexpected row count at insert: ".$numRows);
    }
    return (true);
}

function createProc($conn, $procName, $procArgs, $procCode)
{
    dropProc($conn, $procName);
    $stmt = sqlsrv_query($conn, "CREATE PROC [$procName] ($procArgs) AS BEGIN $procCode END");
    if ($stmt === false) {
        fatalError("Failed to create test procedure");
    }
    sqlsrv_free_stmt($stmt);
}

function dropProc($conn, $procName)
{
    $stmt = sqlsrv_query($conn, "IF OBJECT_ID('". $procName ."', 'P') IS NOT NULL DROP PROCEDURE [$procName]");
    if ($stmt === false) {
    } else {
        sqlsrv_free_stmt($stmt);
    }
}

function callProc($conn, $procName, $procArgs, $procValues)
{
    $stmt = callProcEx($conn, $procName, "", $procArgs, $procValues);
    sqlsrv_free_stmt($stmt);
}

function callProcEx($conn, $procName, $procPrefix, $procArgs, $procValues)
{
    $stmt = sqlsrv_query($conn, "{ $procPrefix CALL [$procName] ($procArgs)}", $procValues);
    if ($stmt === false) {
        fatalError("Failed to call test procedure");
    }
    return ($stmt);
}

function createFunc($conn, $funcName, $funcArgs, $retType, $funcCode)
{
    dropFunc($conn, $funcName);
    $stmt = sqlsrv_query($conn, "CREATE FUNCTION [$funcName] ($funcArgs) RETURNS $retType AS BEGIN $funcCode END");
    if ($stmt === false) {
        fatalError("Failed to create test function");
    }
    sqlsrv_free_stmt($stmt);
}

function dropFunc($conn, $funcName)
{
    $stmt = sqlsrv_query($conn, "DROP FUNCTION [$funcName]");
    if ($stmt === false) {
    } else {
        sqlsrv_free_stmt($stmt);
    }
}

function callFunc($conn, $funcName, $funcArgs, $funcValues)
{
    $stmt = sqlsrv_query($conn, "{ ? = CALL [$funcName]($funcArgs)}", $funcValues);
    if ($stmt === false) {
        fatalError("Failed to call test function");
    }
    sqlsrv_free_stmt($stmt);
}

function fatalError($errorMsg, $print = true) 
{
    SetUTF8Data(false);
    if ($print) {
        printErrors();
    } else {
        handleErrors();
    }
    die($errorMsg."\n");
}

function printErrors($message = "")
{
    if (strlen($message) > 0) {
        echo $message . "\n";
    }
    $errors = sqlsrv_errors(SQLSRV_ERR_ERRORS);
    $count = 0;
    if (!empty($errors)) {
        $count = count($errors);
    } else {
        $errors = sqlsrv_errors(SQLSRV_ERR_ALL);
        if (!empty($errors)) {
            $count = count($errors);
        }
    }

    for ($i = 0; $i < $count; $i++) {
        echo $errors[$i]['message'] . "\n";
    }
}

function handleErrors()
{
    $errors = sqlsrv_errors(SQLSRV_ERR_ERRORS);
    $count = 0;
    if (!empty($errors)) {
        $count = count($errors);
    } else {
        $errors = sqlsrv_errors(SQLSRV_ERR_ALL);
        if (!empty($errors)) {
            $count = count($errors);
        }
    }

    for ($i = 0; $i < $count; $i++) {
        trace($errors[$i]['message']."\n");
    }
}

function setUSAnsiLocale()
{
    // Do not run locale tests if locale disabled
    if (isLocaleDisabled()) {
        return;
    }
    
    if (!isWindows()) {
        // macOS the locale names are different in Linux or macOS
        $locale = strtoupper(PHP_OS) === 'LINUX' ? "en_US.ISO-8859-1" : "en_US.ISO8859-1";
        
        setlocale(LC_ALL, $locale);
    }
}

function resetLocaleToDefault()
{
    // Do not run locale tests if locale disabled
    if (isLocaleDisabled()) {
        return;
    }
    // Like setUSAnsiLocale() above, this method is only needed in non-Windows environment
    if (!isWindows()) {
        setlocale(LC_ALL, null);
    }
}

// non-UTF8 locale support in ODBC 17 and above only
// if AE enabled, only supported in Windows (AE limitations)
function isLocaleSupported()
{
    if (isWindows()) {
        return true;
    }
    // Do not run locale tests if locale disabled
    if (isLocaleDisabled()) {
        return false;
    }
    if (AE\isDataEncrypted()) {
        return false;
    }
    // now check ODBC version
    $conn = AE\connect();
    $msodbcsql_ver = sqlsrv_client_info($conn)['DriverVer'];
    if (explode(".", $msodbcsql_ver)[0] < 17) {
        return false;
    }

    return true;
}

function verifyError($error, $state, $message)
{
    if ($error['SQLSTATE'] !== $state) {
        echo $error['SQLSTATE'] . PHP_EOL;
        fatalError("Unexpected SQL state\n");
    }
    
    if (strpos($error['message'], $message) === false) {
        echo $error['message'] . PHP_EOL;
        fatalError("Unexpected error message\n");
    }
}

function getTodayDateAsString($conn)
{
    $tsql = 'SELECT CONVERT (VARCHAR(20), GETDATE())';
    $stmt = sqlsrv_query($conn, $tsql);
    $result = sqlsrv_fetch($stmt, SQLSRV_FETCH_NUMERIC);
    $today = '';
    if ($result) {
        $today = sqlsrv_get_field($stmt, 0, SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_CHAR));
    } else {
        echo "Failed to get today's date as string: " . PHP_EOL;
        print_r(sqlsrv_errors());
    }
    
    return $today;
}
?>
